/**
 * Copyright (c) 2012 The Chromium Authors. All rights reserved.  Use of this
 * source code is governed by a BSD-style license that can be found in the
 * LICENSE file.
 */

var DEFAULT_DASHBOARD = {
    name: 'privsec_dashboard',
    title: 'CrOS Privacy & Security',
    icons: {
      refresh: 'icons/arrows-arrow-circle-refresh.png'
    },
    modes: [
      {
        name: 'security',
        title: 'Security',
        icon: 'icons/business-lock6-sc48.png',
        views: [
          {
            name: 'sheriff',
            title: 'Sheriff Queue',
            icon: 'icons/transport-travel-transportation-police-car.png',
            default: true,
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Security',
                  'status' : 'Unconfirmed'
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'security_bug_triage',
            title: 'Bug Triage',
            icon: 'icons/transport-travel-transportation-rescue.png',
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Security -label:Feature-Security',
                  'status' : 'Untriaged'
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },


          {
            name: 'security_bugs',  // must be unique and html friendly.
            title: 'Bugs',
            icon: 'icons/signs-gas-mask.png',
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Bug label:Security -label:Feature-Security',
                  'status' : 'Available,Assigned,Started'
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'security_features',
            title: 'Features',
            icon: 'icons/people-things-knife-set-sc43.png',
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Feature-Security,Security label:Type-Task,Type-Feature',
                  'status' : 'Untriaged,Available,Assigned,Started'
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'security_launch',
            title: 'Launch',
            icon: 'icons/transport-travel-space-shuttle.png',
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Launch label:Launch-SecurityReview-No',
                  'status' : 'Started,Fixed,Verified',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Launch label:OS-Chrome label:Launch-SecurityReview-No',
                  'status' : 'Started,Fixed,Verified',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'security_review',
            title: 'Reviews and Audits',
            icon: 'icons/business-microscope.png',
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-SecurityReview',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-SecurityReview label:OS-Chrome',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'security_backlog',
            title: 'Backlog',
            icon: 'icons/people-things-faucet2-sc52.png',
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Security-NCA',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
        ],
      },
      {
        name: 'privacy',
        title: 'Privacy',
        icon: 'icons/people-things-eye.png',
        views: [
          {
            name: 'privacy_launch',
            title: 'Launch',
            icon: 'icons/transport-travel-space-shuttle.png',
            default: true,
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Launch label:Launch-PrivacyReview-No',
                  'status' : 'Started,Fixed,Verified',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Launch label:OS-Chrome label:Launch-PrivacyReview-No',
                  'status' : 'Started,Fixed,Verified',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'privacy_review',
            title: 'Reviews and Audits',
            icon: 'icons/business-microscope.png',
            requests: {
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-PrivacyReview',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-PrivacyReview label:OS-Chrome',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
        ],
      },
      {
        name: 'personal',
        title: 'Personal',
        icon: 'icons/people-things-speech.png',
        default: true,
        views: [
          {
            name: 'personal_blockers',
            title: 'Blockers',
            icon: 'icons/people-things-people-security-guard-49.png',
            default: true,
            requests: {
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:ReleaseBlock-stable,ReleaseBlock-beta,ReleaseBlock-dev',
                  'can' : 'owned',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:ReleaseBlock-stable,ReleaseBlock-beta,ReleaseBlock-dev',
                  'can' : 'owned',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'personal_features',
            title: 'Features',
            icon: 'icons/people-things-knife-set-sc43.png',
            requests: {
              // chromium-os, chromium, ...
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Task,Type-Feature,Type-Test',
                  'can' : 'owned',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Task,Type-Feature,Type-Test',
                  'can': 'owned',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'personal_bugs',
            title: 'Bugs',
            icon: 'icons/signs-gas-mask.png',
            requests: {
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Bug',
                  'can' : 'owned',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-Bug',
                  'can' : 'owned',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
          {
            name: 'personal_reviews',
            title: 'Reviews and Audits',
            icon: 'icons/business-microscope.png',
            requests: {
              'https://code.google.com/feeds/issues/p/chromium-os/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-PrivacyReview,Type-SecurityReview',
                  'can' : 'owned',
                }
              },
              'https://code.google.com/feeds/issues/p/chromium/issues/full' : {
                'method': 'GET',
                'parameters': {
                  'alt': 'json',
                  'start-index' : 1,
                  'max-results' : 100,
                  'q' : 'label:Type-PrivacyReview,Type-SecurityReview label:OS-Chrome',
                  'can' : 'owned',
                }
              },
            },
            actions: [ 'open', 'confirm', 'wont-fix', 'invalid' ],  // TODO: TBD makes buttons
          },
       ],
      },

    ],
  };


function loadDashboard() {
  var dash = localStorage["dashboard"];
  if (!dash) {
    dash = DEFAULT_DASHBOARD;
    localStorage["dashboard"] = JSON.stringify(dash);
  } else {
    dash = JSON.parse(dash);
  }
  return dash;
}

function refreshBindings() {
  $('article.tabs section > .button').unbind('click');
  $('article.tabs section > .button').click(function(){
    $('article.tabs.current section').removeClass('current');
    $(this).closest('section').addClass('current');
    // Load this section if needed.
    updateView($(this).closest('section').children('.content').attr('id'));
  });

  $('div.modes div > img').unbind('click');
  $('div.modes div > img').click(function(){
    $('div.modes div').removeClass('current');
    $(this).closest('div').addClass('current');
    var mode = $(this).closest('div').attr("mode");
    $('article.tabs').removeClass('current');
    $('article.tabs.mode-' + mode).addClass('current');
    // Update all sections for the now-current mode.
    $('article.tabs.current section div.content').each(function(index, element) {
      updateView(element.id);
    });
  });

  $('div.issue div.label').unbind('click');
  $('div.issue div.label').click(function(){
    var issue = $(this).closest('.issue');
    var needs = issue.hasClass('current') == false;
    $('div.issue').removeClass('current');
    if (needs) {
      issue.addClass('current');
      updateComments(issue.attr('id') + '/comments');
    }
    // Scroll the label into view.
    issue[0].childNodes[0].scrollIntoViewIfNeeded();
  });

  $('#refresh-button').unbind('click');
  $('#refresh-button').click(function() {
    $('article.tabs section div.content').each(function(index, element) {
      updateView(element.id, true);
    });
  });
}


function createModeSelector(d, parent) {
  var topbar = document.createElement('div');
  topbar.className = 'mode-selector';
  parent.appendChild(topbar);

  var refresh = document.createElement('div');
  refresh.className = 'refresh';
  refresh.id = 'refresh-button';
  topbar.appendChild(refresh);

  // XXX: probably kill this functionality.
  var refresh_icon = document.createElement('img');
  refresh_icon.width = 75;
  refresh_icon.height = 75;
  refresh_icon.src = d.icons.refresh;
  refresh.appendChild(refresh_icon);

  var modes = document.createElement('div');
  modes.className = 'modes';
  topbar.appendChild(modes);

  for (var m = 0; m < d.modes.length; ++m) {
    var mode = document.createElement('div');
    mode.id = d.modes[m].name + '-mode-button';
    mode.className = 'mode';
    if (d.modes[m].default == true) {
      mode.className += ' current';
    }
    // TODO(wad) just attach the click handler here unless it is worth it to do layout separately.
    mode.setAttribute('mode', d.modes[m].name);
    var icon = document.createElement('img');
    icon.width = 75;  // XXX: make icons dimensions configurable.
    icon.height = 75;
    icon.src = d.modes[m].icon;
    icon.title = d.modes[m].title;
    mode.appendChild(icon);
    modes.appendChild(mode);
  }
}

// Lays out articles and sections for a given mode.
function createModeViews(mode, parent) {
  var article = document.createElement('article');
  article.className = 'tabs mode-' + mode.name;
  if (mode.default == true)
    article.className += ' current';
  for (var i = 0; i < mode.views.length; ++i) {
    var view = mode.views[i];
    var section = document.createElement('section');
    section.className = 'view-' + view.name;
    section.id = 'view-' + view.name;
    if (view.default == true)
      section.className += ' current';
    article.appendChild(section);
    var button = document.createElement('div');
    button.className = 'button';
    section.appendChild(button);
    var icon = document.createElement('img');
    icon.src = view.icon;
    icon.width = 75;
    icon.height = 75;
    icon.title = view.title;
    button.appendChild(icon);
    var badge = document.createElement('div');
    badge.className = 'badge';
    // Break naming scheme to make it easier to fine.
    badge.id = 'view-badge-' + view.name;
    badge.innerText = '0';
    button.appendChild(badge);
    var content = document.createElement('div');
    content.className = 'content';
    content.id = 'view-content-' + view.name;
    content.setAttribute('load', true);
    content.setAttribute('self', JSON.stringify(view.requests));
    // Pass in the action names for each issue in this view
    content.setAttribute('actions', JSON.stringify(view.actions));
    section.appendChild(content);
  }
  parent.appendChild(article);
}

// TODO(wad) take an array for the value too
function createFieldElement(key, values, desc) {
  var field = document.createElement('div');
  field.className = 'field field-' + key;
  var field_label = document.createElement('div');
  field_label.className = 'field-label field-label-' + key;
  field_label.innerText = desc;
  field.appendChild(field_label);

  if (values.constructor != Array) {
    if (values.$t != null)
      values = [{'$t':values.$t}];
    else
      values = [values];
  }

  for (var v = 0; v < values.length; ++v) {
    var key_content = document.createElement('div');
    key_content.className = 'field-value field-value-' + key;
    if (values[v].$t != null) {
      key_content.innerText = values[v].$t;
    } else if (values[v].issues$username) {
      key_content.innerText = values[v].issues$username.$t;
    } else if (values[v].issues$id) {
      key_content.innerText = values[v].issues$id.$t;
    } else {
      key_content.innerText = JSON.stringify(values[v]);
    }
    field.appendChild(key_content);
  }
  return field;
}

function populateView(content, data) {
  console.log("Populating view: " + content.parentNode.id);
  var total = Number(data.feed.openSearch$totalResults.$t);
  var limit = data.feed.openSearch$itemsPerPage.$t;
  var index = Number(data.feed.openSearch$startIndex.$t) - 1;

  // TODO(wad) split out badge updates so that we can just do badge updates in the background
  // and not load the full view state.
  var badge = document.getElementById(content.id.replace('view-content', 'view-badge'));
  var count = Number(badge.innerText) + total;
  badge.innerText = String(count);

  // TODO(wad) Technically, this is a race because multiple async calls may populate
  // the badge.  For now, that's fine.  Later, they can be populate by the childElementCount
  // during the refreshBindings call.
  if (count == 0) {
    // TODO(wad) do this without jquery.
    $(content.parentNode).removeClass('work');
    return true;
  } else {
    $(content.parentNode).addClass('work');
  }

  if (total == 0) {
    return true;
  }

  for (var i = 0; i < data.feed.entry.length; ++i) {
    var entry = data.feed.entry[i];
    var container = document.createElement('div');
    container.id = entry.id.$t;
    container.className = 'issue';

    // Make the issues sortable and color-able by mstone.
    var mstone = entry.issues$label.sort(function(a,b) { return b.$t.search('^Mstone') != null; });
    for (var m = 0; m < mstone.length; ++m) {
      if (mstone[m].$t.search('^Mstone') == null);
        break;
      container.className += ' ' + mstone[m].$t.toLowerCase();
    }

    var label = document.createElement('div');
    label.className = 'label';
    container.appendChild(label);

    var id = document.createElement('span');
    id.className = 'id';
    id.innerText = entry.issues$id.$t;
    label.appendChild(id);

    var title = document.createElement('span');
    title.className = 'title';
    title.innerText = entry.title.$t;
    label.appendChild(title);

    // TODO(wad) color and order based on.
    // entry.issues$label[0].$t (e.g., Type-Bug)
    var restricted = entry.issues$label.sort(function(a,b) { return b.$t == "Restrict-View-SecurityTeam"; });
    if (restricted.length > 0 && restricted[0].$t == "Restrict-View-SecurityTeam") {
      container.className += ' restrict-view-securityteam';
    }

    var actions = document.createElement('span');
    actions.className = 'actions';
    label.appendChild(actions);

    var action_names = JSON.parse(content.getAttribute('actions'));
    for (var a = 0; a < action_names.length; ++a) {
      var action = action_names[a];
      if (action == 'open') {
        var open = document.createElement('span');
        open.className = 'action-open';
        var link = entry.link.sort(function(a,b) { return b.rel == "alternate"; })[0];
        open.setAttribute('href', link.href);
        var actionOpen = function(e) {
          var href = e.target.getAttribute('href');
          window.open(href, '', '');
        };
        open.addEventListener('click', actionOpen, false);
        open.innerText = 'open';
        actions.appendChild(open);
      }
    }

    var details = document.createElement('div');
    details.className = 'details';
    container.appendChild(details);

    var desc = createFieldElement('description', entry.content, 'Description');
    details.appendChild(desc);

    // For now, just enumerate the issues values.
    for (prop in entry) {
      if (prop.substr(0, 7) == 'issues$') {
        var name = prop.substr(7, prop.length);
        var kv = createFieldElement(prop, entry[prop], name);
        details.appendChild(kv);
      }
    }

    var comments = document.createElement('div');
    comments.className = 'comments';
    comments.id = entry.id.$t + '/comments';
    var replies = String(entry.link.sort(function(a,b) { return b.rel == "replies"; })[0].href);
    comments.setAttribute('replies', replies);
    comments.setAttribute('load', 'true');
    details.appendChild(comments);

    content.appendChild(container);
  }

  return true;
}

function anchorExists(v) {
 return document.location.hash.match('[^,]' + v + '(,|$)') != null
}

function populateComments(container, data) {
  for (var i = 0; i < data.feed.entry.length; ++i) {
    var entry = data.feed.entry[i];
    var comment = document.createElement('div');
    comment.className = 'comment';
    comment.id = entry.id.$t;
    container.appendChild(comment);

    var field = createFieldElement('author', entry.author[0].name, 'Author');
    comment.appendChild(field);

    field = createFieldElement('updated', entry.updated, 'Last updated');
    comment.appendChild(field);

    field = createFieldElement('content', entry.content, 'Comment');
    comment.appendChild(field);

    // Grab the non-text updates
    for (prop in entry.issues$updates) {
      if (prop.substr(0, 7) == 'issues$') {
        var k = prop.substr(7, prop.length);
        var kv = createFieldElement(k, entry.issues$updates[prop], k + ' ');
        comment.appendChild(kv);
      }
    }
  }
  return true;
}

function loadComments(url, callback) {
  var request = {
    'method': 'GET',
    'parameters': {
      'alt': 'json'
    }
  };
  var callback_wrapper = function (data, xhr) {
    callback(JSON.parse(data));
  };
  if (bgPage) {
    if (url.substr(0,5) == 'http:') {
      url = 'https:' + url.substr(5, url.length);
    }
    console.log("Loading comments from " + url);
    bgPage.oauth.sendSignedRequest(url, callback_wrapper, request);
  } else {
    console.log("No bgPage! Using mock if available");
    callback(mock_comments);
  }
}



function updateComments(id, force) {
  var comments = document.getElementById(id);
  if (comments == null) {
    console.log("No comments element found for " + id);
    return false;
  }
  var needs_refresh = comments.getAttribute('load') == 'true';
  if (!needs_refresh && !force) {
    console.log("Comments already loaded for " + id);
    return true;
  }

  var doit = function(json_data) {
    comments.innerText = '';
    populateComments(comments, json_data);
    comments.setAttribute('load', 'false');
    refreshBindings();
  };

  // Use the replies attribute to smuggle in the URL.
  data = loadComments(comments.getAttribute('replies'), doit);
  return true;
}

function loadView(requests, callback) {
  for (url in requests) {
    var request = requests[url];
    console.log("Sending request to " + url + ": " + JSON.stringify(request));
    var callback_wrapper = function (data, xhr) {
      callback(JSON.parse(data));
    };
    if (bgPage) {
      bgPage.oauth.sendSignedRequest(url, callback_wrapper, request);
    } else {
      console.log("No bgPage! Using mock if available");
      callback(mock_query);
    }
  }
}

function updateView(id, force) {
  // view-content-|name|
  var view = document.getElementById(id);
  if (view == null) {
    console.log("No view element found for " + id);
    return false;
  }
  var needs_refresh = view.getAttribute('load') == 'true';
  if (!needs_refresh && !force)
    return true;

  var doit = function(json_data) {
    populateView(view, json_data);
    view.setAttribute('load', 'false');
    refreshBindings();
  };

  // Grab the Requests object.
  var badge = view.previousSibling.childNodes[1];
  if (badge.className != 'badge') {
    console.log("previousSibling.childNodes[1] was wrong");
  } else {
    badge.innerText = '0';
  }
  view.innerText = '';
  res = loadView(JSON.parse(view.getAttribute('self')), doit);
}

function createDashboard(data, p) {
  createModeSelector(data, p);

  // Do painful abckground page oauth here.

  for (var i = 0; i < data.modes.length; ++i) {
    createModeViews(data.modes[i], p);
  }
}

// Pre-load everything in the viewable tab including the non-viewable sections.
function makeWorld(dashboard, p) {
  createDashboard(dashboard, p);
  $('article.tabs.current section div.content').each(function(index, element) {
    updateView(element.id);
  });
}


